 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  * Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog
  * font should be activated and used by other components.
  *******************************************************************************/

 package org.eclipse.ui.dialogs;

 import java.lang.reflect.InvocationTargetException ;
 import java.net.URI ;
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IProjectNatureDescriptor;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IWorkspace;
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Preferences;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.actions.WorkspaceModifyOperation;
 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
 import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
 import org.eclipse.ui.internal.ide.dialogs.CreateLinkedResourceGroup;

 /**
  * The NewFolderDialog is used to create a new folder.
  * The folder can optionally be linked to a file system folder.
  * <p>
  * This class may be instantiated; it is not intended to be subclassed.
  * </p>
  */
 public class NewFolderDialog extends SelectionStatusDialog {
     // widgets
 private Text folderNameField;

     private Button advancedButton;

     private CreateLinkedResourceGroup linkedResourceGroup;

     private IContainer container;

     private boolean firstLinkCheck = true;

     /**
      * Parent composite of the advanced widget group for creating
      * linked resources.
      */
     private Composite linkedResourceParent;

     /**
      * Linked resources widget group. Null if advanced section is not visible.
      */
     private Composite linkedResourceComposite;

     /**
      * Height of the dialog without the "advanced" linked resource group.
      * Set when the advanced group is first made visible.
      */
     private int basicShellHeight = -1;

     /**
      * Creates a NewFolderDialog
      *
      * @param parentShell parent of the new dialog
      * @param container parent of the new folder
      */
     public NewFolderDialog(Shell parentShell, IContainer container) {
         super(parentShell);
         this.container = container;
         setTitle(IDEWorkbenchMessages.NewFolderDialog_title);
         setShellStyle(getShellStyle() | SWT.RESIZE);
         setStatusLineAboveButtons(true);
     }

     /**
      * Creates the folder using the name and link target entered
      * by the user.
      * Sets the dialog result to the created folder.
      */
     protected void computeResult() {
         //Do nothing here as we
 //need to know the result
 }

     /* (non-Javadoc)
      * Method declared in Window.
      */
     protected void configureShell(Shell shell) {
         super.configureShell(shell);
         PlatformUI.getWorkbench().getHelpSystem().setHelp(shell,
                 IIDEHelpContextIds.NEW_FOLDER_DIALOG);
     }

     /**
      * @see org.eclipse.jface.window.Window#create()
      */
     public void create() {
         super.create();
         // initially disable the ok button since we don't preset the
 // folder name field
 getButton(IDialogConstants.OK_ID).setEnabled(false);
     }

     /**
      * Creates the widget for advanced options.
      *
      * @param parent the parent composite
      */
     protected void createAdvancedControls(Composite parent) {
         Preferences preferences = ResourcesPlugin.getPlugin()
                 .getPluginPreferences();

         if (preferences.getBoolean(ResourcesPlugin.PREF_DISABLE_LINKING) == false
                 && isValidContainer()) {
             linkedResourceParent = new Composite(parent, SWT.NONE);
             linkedResourceParent.setFont(parent.getFont());
             linkedResourceParent.setLayoutData(new GridData(
                     GridData.FILL_HORIZONTAL));
             GridLayout layout = new GridLayout();
             layout.marginHeight = 0;
             layout.marginWidth = 0;
             linkedResourceParent.setLayout(layout);

             advancedButton = new Button(linkedResourceParent, SWT.PUSH);
             advancedButton.setFont(linkedResourceParent.getFont());
             advancedButton.setText(IDEWorkbenchMessages.showAdvanced);
             setButtonLayoutData(advancedButton);
             GridData data = (GridData) advancedButton.getLayoutData();
             data.horizontalAlignment = GridData.BEGINNING;
             advancedButton.setLayoutData(data);
             advancedButton.addSelectionListener(new SelectionAdapter() {
                 public void widgetSelected(SelectionEvent e) {
                     handleAdvancedButtonSelect();
                 }
             });
         }
         linkedResourceGroup = new CreateLinkedResourceGroup(IResource.FOLDER,
                 new Listener() {
                     public void handleEvent(Event e) {
                         validateLinkedResource();
                         firstLinkCheck = false;
                     }
                 }, new CreateLinkedResourceGroup.IStringValue() {
                     public void setValue(String string) {
                         folderNameField.setText(string);
                     }

                     public String getValue() {
                         return folderNameField.getText();
                     }
                 });
     }

     /* (non-Javadoc)
      * Method declared on Dialog.
      */
     protected Control createDialogArea(Composite parent) {
         Composite composite = (Composite) super.createDialogArea(parent);
         composite.setLayout(new GridLayout());
         composite.setLayoutData(new GridData(GridData.FILL_BOTH));

         createFolderNameGroup(composite);
         createAdvancedControls(composite);
         return composite;
     }

     /**
      * Creates the folder name specification controls.
      *
      * @param parent the parent composite
      */
     private void createFolderNameGroup(Composite parent) {
         Font font = parent.getFont();
         // project specification group
 Composite folderGroup = new Composite(parent, SWT.NONE);
         GridLayout layout = new GridLayout();
         layout.numColumns = 2;
         folderGroup.setLayout(layout);
         folderGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

         // new folder label
 Label folderLabel = new Label(folderGroup, SWT.NONE);
         folderLabel.setFont(font);
         folderLabel.setText(IDEWorkbenchMessages.NewFolderDialog_nameLabel);

         // new folder name entry field
 folderNameField = new Text(folderGroup, SWT.BORDER);
         GridData data = new GridData(GridData.FILL_HORIZONTAL);
         data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
         folderNameField.setLayoutData(data);
         folderNameField.setFont(font);
         folderNameField.addListener(SWT.Modify, new Listener() {
             public void handleEvent(Event event) {
                 validateLinkedResource();
             }
         });
     }

     /**
      * Creates a folder resource handle for the folder with the given name.
      * The folder handle is created relative to the container specified during
      * object creation.
      *
      * @param folderName the name of the folder resource to create a handle for
      * @return the new folder resource handle
      */
     private IFolder createFolderHandle(String folderName) {
         IWorkspaceRoot workspaceRoot = container.getWorkspace().getRoot();
         IPath folderPath = container.getFullPath().append(folderName);
         IFolder folderHandle = workspaceRoot.getFolder(folderPath);

         return folderHandle;
     }

     /**
      * Creates a new folder with the given name and optionally linking to
      * the specified link target.
      *
      * @param folderName name of the new folder
      * @param linkTarget name of the link target folder. may be null.
      * @return IFolder the new folder
      */
     private IFolder createNewFolder(String folderName, final URI linkTarget) {
         final IFolder folderHandle = createFolderHandle(folderName);

         WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
             public void execute(IProgressMonitor monitor) throws CoreException {
                 try {
                     monitor
                             .beginTask(
                                     IDEWorkbenchMessages.NewFolderDialog_progress,
                                     2000);
                     if (monitor.isCanceled()) {
                         throw new OperationCanceledException();
                     }
                     if (linkTarget == null) {
                         folderHandle.create(false, true, monitor);
                     } else {
                         folderHandle.createLink(linkTarget,
                                 IResource.ALLOW_MISSING_LOCAL, monitor);
                     }
                     if (monitor.isCanceled()) {
                         throw new OperationCanceledException();
                     }
                 } finally {
                     monitor.done();
                 }
             }
         };
         try {
             PlatformUI.getWorkbench().getProgressService().busyCursorWhile(
                     operation);
         } catch (InterruptedException exception) {
             return null;
         } catch (InvocationTargetException exception) {
             if (exception.getTargetException() instanceof CoreException) {
                 ErrorDialog.openError(getShell(),
                         IDEWorkbenchMessages.NewFolderDialog_errorTitle, null, // no special message
 ((CoreException) exception.getTargetException())
                                 .getStatus());
             } else {
                 // CoreExceptions are handled above, but unexpected runtime exceptions and errors may still occur.
 IDEWorkbenchPlugin.log(getClass(),
                         "createNewFolder", exception.getTargetException()); //$NON-NLS-1$
 MessageDialog
                         .openError(
                                 getShell(),
                                 IDEWorkbenchMessages.NewFolderDialog_errorTitle,
                                 NLS
                                         .bind(
                                                 IDEWorkbenchMessages.NewFolderDialog_internalError,
                                                 exception.getTargetException()
                                                         .getMessage()));
             }
             return null;
         }
         return folderHandle;
     }

     /**
      * Shows/hides the advanced option widgets.
      */
     protected void handleAdvancedButtonSelect() {
         Shell shell = getShell();
         Point shellSize = shell.getSize();
         Composite composite = (Composite) getDialogArea();

         if (linkedResourceComposite != null) {
             linkedResourceComposite.dispose();
             linkedResourceComposite = null;
             composite.layout();
             shell.setSize(shellSize.x, basicShellHeight);
             advancedButton.setText(IDEWorkbenchMessages.showAdvanced);
         } else {
             if (basicShellHeight == -1) {
                 basicShellHeight = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT,
                         true).y;
             }
             linkedResourceComposite = linkedResourceGroup
                     .createContents(linkedResourceParent);
             shellSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
             shell.setSize(shellSize);
             composite.layout();
             advancedButton.setText(IDEWorkbenchMessages.hideAdvanced);
         }
     }

     /**
      * Returns whether the container specified in the constructor is
      * a valid parent for creating linked resources.
      *
      * @return boolean <code>true</code> if the container specified in
      * the constructor is a valid parent for creating linked resources.
      * <code>false</code> if no linked resources may be created with the
      * specified container as a parent.
      */
     private boolean isValidContainer() {
         if (container.getType() != IResource.PROJECT
                 && container.getType() != IResource.FOLDER) {
             return false;
         }

         try {
             IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
             IProject project = container.getProject();
             String [] natureIds = project.getDescription().getNatureIds();

             for (int i = 0; i < natureIds.length; i++) {
                 IProjectNatureDescriptor descriptor = workspace
                         .getNatureDescriptor(natureIds[i]);
                 if (descriptor != null
                         && descriptor.isLinkingAllowed() == false) {
                     return false;
                 }
             }
         } catch (CoreException exception) {
             // project does not exist or is closed
 return false;
         }
         return true;
     }

     /**
      * Update the dialog's status line to reflect the given status. It is safe to call
      * this method before the dialog has been opened.
      */
     protected void updateStatus(IStatus status) {
         if (firstLinkCheck && status != null) {
             // don't show the first validation result as an error.
 // fixes bug 29659
 Status newStatus = new Status(IStatus.OK, status.getPlugin(),
                     status.getCode(), status.getMessage(), status
                             .getException());
             super.updateStatus(newStatus);
         } else {
             super.updateStatus(status);
         }
     }

     /**
      * Update the dialog's status line to reflect the given status. It is safe to call
      * this method before the dialog has been opened.
      * @param severity
      * @param message
      */
     private void updateStatus(int severity, String message) {
         updateStatus(new Status(severity, IDEWorkbenchPlugin.IDE_WORKBENCH,
                 severity, message, null));
     }

     /**
      * Checks whether the folder name and link location are valid.
      * Disable the OK button if the folder name and link location are valid.
      * a message that indicates the problem otherwise.
      */
     private void validateLinkedResource() {
         boolean valid = validateFolderName();

         if (valid) {
             IFolder linkHandle = createFolderHandle(folderNameField.getText());
             IStatus status = linkedResourceGroup
                     .validateLinkLocation(linkHandle);

             if (status.getSeverity() != IStatus.ERROR) {
                 getOkButton().setEnabled(true);
             } else {
                 getOkButton().setEnabled(false);
             }

             if (status.isOK() == false) {
                 updateStatus(status);
             }
         } else {
             getOkButton().setEnabled(false);
         }
     }

     /**
      * Checks if the folder name is valid.
      *
      * @return null if the new folder name is valid.
      * a message that indicates the problem otherwise.
      */
     private boolean validateFolderName() {
         String name = folderNameField.getText();
         IWorkspace workspace = container.getWorkspace();
         IStatus nameStatus = workspace.validateName(name, IResource.FOLDER);

         if ("".equals(name)) { //$NON-NLS-1$
 updateStatus(IStatus.ERROR,
                     IDEWorkbenchMessages.NewFolderDialog_folderNameEmpty);
             return false;
         }
         if (nameStatus.isOK() == false) {
             updateStatus(nameStatus);
             return false;
         }
         IPath path = new Path(name);
         if (container.getFolder(path).exists()
                 || container.getFile(path).exists()) {
             updateStatus(IStatus.ERROR, NLS.bind(
                     IDEWorkbenchMessages.NewFolderDialog_alreadyExists, name));
             return false;
         }
         updateStatus(IStatus.OK, ""); //$NON-NLS-1$
 return true;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.dialogs.SelectionStatusDialog#okPressed()
      */
     protected void okPressed() {
         URI linkTarget = linkedResourceGroup.getLinkTargetURI();
         IFolder folder = createNewFolder(folderNameField.getText(), linkTarget);
         if (folder == null) {
             return;
         }

         setSelectionResult(new IFolder[] { folder });

         super.okPressed();
     }
 }

